package com.hgl.bi.web.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hgl.bi.common.constant.BusinessConstant;
import com.hgl.bi.common.constant.ErrorCode;
import com.hgl.bi.common.constant.LoginStatus;
import com.hgl.bi.common.exception.BusinessException;
import com.hgl.bi.common.manager.COSManager;
import com.hgl.bi.common.manager.CaptchaManager;
import com.hgl.bi.common.manager.RedisManager;
import com.hgl.bi.common.utils.AssertUtil;
import com.hgl.bi.common.utils.StrUtil;
import com.hgl.bi.web.entity.RoleEntity;
import com.hgl.bi.web.entity.UserEntity;
import com.hgl.bi.web.mapper.UserMapper;
import com.hgl.bi.web.model.dto.LoginDto;
import com.hgl.bi.web.model.dto.RegisterDto;
import com.hgl.bi.web.model.dto.UserInfoDto;
import com.hgl.bi.web.service.RoleService;
import com.hgl.bi.web.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

/**
 * @program: BI
 * @description: 用户服务实现层
 * @author: hgl
 * @create: 2025-02-12 16:11
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity>
        implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CaptchaManager captchaManager;

    @Autowired
    private COSManager cosManager;

    @Autowired
    private RoleService roleService;

    @Autowired
    private RedisManager redisManager;

    @Override
    public void register(RegisterDto registerDto) {
        String email = registerDto.getEmail();
        String phone = registerDto.getPhone();
        String password = registerDto.getPassword();
        String checkPwd = registerDto.getCheckPwd();
        // 非空校验
        AssertUtil.notEmpty(email, ErrorCode.EMAIL_NOT_NULL);
        AssertUtil.notEmpty(phone, ErrorCode.PHONE_NOT_NULL);
        AssertUtil.notEmpty(password, ErrorCode.PASSWORD_NOT_NULL);
        AssertUtil.notEmpty(checkPwd, ErrorCode.CHECK_PWD_NOT_NULL);
        // 参数校验
        AssertUtil.isTrue(email.matches(BusinessConstant.EMAIL_REGEX), ErrorCode.EMAIL_FORMAT_ERROR);
        AssertUtil.isTrue(phone.matches(BusinessConstant.PHONE_REGEX), ErrorCode.PHONE_FORMAT_ERROR);
        AssertUtil.isTrue(password.length() >= 6, ErrorCode.PASSWORD_LENGTH_ERROR);
        AssertUtil.isTrue(password.equals(checkPwd), ErrorCode.PASSWORD_NOT_EQUALS);
        // 查询用户是否存在
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<UserEntity>()
                .eq("email", email).or().eq("phone", phone);
        UserEntity user = this.getOne(wrapper);
        AssertUtil.isNull(user, ErrorCode.USER_EXISTS);
        // 保存用户信息
        String encrypted = StrUtil.encryptByMd5(password);
        user = new UserEntity();
        user.setUsername("访客" + phone.substring(phone.length() - 4));
        user.setPassword(encrypted);
        user.setEmail(email);
        user.setPhone(phone);
        user.setAvatar(BusinessConstant.DEFAULT_AVATAR);
        user.setRegisterTime(new Date());
        boolean saved = this.save(user);
        AssertUtil.isTrue(saved, ErrorCode.REGISTER_FALSE);
    }

    @Override
    public String login(LoginDto loginDto) {
        String account = loginDto.getAccount();
        String password = loginDto.getPassword();
        String captcha = loginDto.getCaptcha();
        String key = loginDto.getKey();
        Boolean rememberMe = loginDto.getRememberMe();
        // 非空校验
        AssertUtil.notEmpty(account, ErrorCode.ACCOUNT_NOT_NULL);
        AssertUtil.notEmpty(password, ErrorCode.PASSWORD_NOT_NULL);
        AssertUtil.notEmpty(captcha, ErrorCode.CAPTCHA_NOT_NULL);
        AssertUtil.notEmpty(key, ErrorCode.REQUEST_PARAM_ERROR);
        AssertUtil.notNull(rememberMe, ErrorCode.REQUEST_PARAM_ERROR);
        // 参数校验
        AssertUtil.isTrue(captchaManager.validateCaptcha(key, captcha), ErrorCode.CAPTCHA_ERROR);
        AssertUtil.isTrue(password.length() >= 6, ErrorCode.PASSWORD_LENGTH_ERROR);

        // 查询用户是否存在
        String encrypted = StrUtil.encryptByMd5(password);
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
        if (account.matches(BusinessConstant.PHONE_REGEX)) {
            wrapper.eq("phone", account).eq("password", encrypted);
        } else {
            wrapper.eq("email", account).eq("password", encrypted);
        }
        UserEntity user = this.getOne(wrapper);
        AssertUtil.notNull(user, ErrorCode.ACCOUNT_OR_PASSWORD_ERROR);
        // 更新用户状态，成功登录，返回用户ID
        UpdateWrapper<UserEntity> updateWrapper = new UpdateWrapper<UserEntity>()
                .eq("id", user.getId())
                .set("status", LoginStatus.LOGIN.getStatus());
        // 更新最后登录时间
        updateWrapper.set("last_login_time", new Date());
        boolean updated = this.update(updateWrapper);
        AssertUtil.isTrue(updated, ErrorCode.LOGIN_FALSE);
        // 查询用户角色
        QueryWrapper<RoleEntity> roleWrapper = new QueryWrapper<RoleEntity>()
                .eq("id", user.getRoleId());
        RoleEntity role = roleService.getOne(roleWrapper);
        if (rememberMe) {
            // 登录
            StpUtil.login(user.getId(), new SaLoginModel().setTimeout(60 * 60 * 24 * 7));
            // 持久化用户信息
            UserInfoDto userInfo = new UserInfoDto();
            userInfo.setId(user.getId());
            userInfo.setUsername(user.getUsername());
            String url = cosManager.getAccessSignature(user.getAvatar(), BusinessConstant.A_WEEK);
            userInfo.setAvatar(url);
            userInfo.setRole(role.getRoleCode());

            redisManager.setObj("satoken:" + StpUtil.getTokenValue(),
                    userInfo, 60 * 60 * 24 * 7);
        } else {
            // 登录
            StpUtil.login(user.getId(), false);

            // 持久化用户信息
            UserInfoDto userInfo = new UserInfoDto();
            userInfo.setId(user.getId());
            userInfo.setUsername(user.getUsername());
            String url = cosManager.getAccessSignature(user.getAvatar(), BusinessConstant.ONE_DAY);
            userInfo.setAvatar(url);
            userInfo.setRole(role.getRoleCode());

            redisManager.setObj("satoken:" + StpUtil.getTokenValue(),
                    userInfo, 60 * 60 * 24);
        }

        return StpUtil.getTokenValue();
    }

    @Override
    public void logout(Long userId) {
        // 更新用户状态
        UpdateWrapper<UserEntity> wrapper = new UpdateWrapper<UserEntity>()
                .eq("id", userId)
                .set("status", LoginStatus.LOGOUT.getStatus());
        boolean updated = this.update(wrapper);
        AssertUtil.isTrue(updated, ErrorCode.LOGOUT_FALSE);
    }

    @Override
    public void checkToken(String satoken) {
        UserInfoDto userInfo = (UserInfoDto) redisManager
                .get("satoken:" + satoken, UserInfoDto.class);

        if (userInfo == null) {
            // 更新用户状态
            UpdateWrapper<UserEntity> wrapper = new UpdateWrapper<UserEntity>()
                    .eq("id", userInfo.getId())
                    .set("status", LoginStatus.LOGOUT.getStatus());
            boolean updated = this.update(wrapper);
            AssertUtil.isTrue(updated, ErrorCode.LOGOUT_FALSE);
            throw new BusinessException(ErrorCode.TOKEN_INVALID);
        }
        // 更新用户状态
        UpdateWrapper<UserEntity> wrapper = new UpdateWrapper<UserEntity>()
                .eq("id", userInfo.getId())
                .set("status", LoginStatus.LOGIN.getStatus());
        boolean updated = this.update(wrapper);
        AssertUtil.isTrue(updated, ErrorCode.LOGIN_FALSE);
    }

    @Override
    public List<String> getUserPermissions(Long userId) {
        List<String> permissions = userMapper.selectUserPermissions(userId);
        return permissions;
    }

}
